{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Populating the interactive namespace from numpy and matplotlib\n"
     ]
    }
   ],
   "source": [
    "%pylab inline\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torchvision.transforms as transforms\n",
    "import torchvision.datasets as dsets\n",
    "\n",
    "import sys\n",
    "from tqdm import tqdm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "cuda = torch.cuda.is_available()\n",
    "device = torch.device(\"cuda\" if cuda else \"cpu\")\n",
    "save_path = 'cache/models'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([60000, 28, 28])\n",
      "torch.Size([10000, 28, 28])\n",
      "tensor(5842) tensor(6265)\n",
      "tensor(982) tensor(1028)\n"
     ]
    }
   ],
   "source": [
    "train_dataset = dsets.MNIST(root='./data', \n",
    "                            train=True, \n",
    "                            transform=transforms.ToTensor(),\n",
    "                            download=True)\n",
    "test_dataset = dsets.MNIST(root='./data', \n",
    "                           train=False, \n",
    "                           transform=transforms.ToTensor())\n",
    "print(train_dataset.data.size())\n",
    "print(test_dataset.data.size())\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "digit_p, digit_q =  4, 7\n",
    "\n",
    "# digit_p, digit_q =  4,9\n",
    "\n",
    "\n",
    "data1 = train_dataset\n",
    "# selecting number 0 zero only\n",
    "tt = data1.targets[(data1.targets== digit_p) | (data1.targets== digit_q)]\n",
    "tt[tt==digit_p] =  0\n",
    "tt[tt==digit_q] = 1\n",
    "dd = data1.data[(data1.targets== digit_p) | (data1.targets== digit_q)] \n",
    "# tt = data.targets[(data.targets== 1)]\n",
    "# dd = data.data[(data.targets== 1)] \n",
    "\n",
    "data1.targets = tt\n",
    "data1.data = dd\n",
    "train_loader = torch.utils.data.DataLoader(data1, batch_size=100, shuffle=True, drop_last = True)\n",
    "# Num batches\n",
    "# num_batches = len(train_loader)\n",
    "\n",
    "print((tt==0).sum(), (tt==1).sum())\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "data2 = test_dataset\n",
    "# selecting number 0 zero only\n",
    "tt = data2.targets[(data2.targets== digit_p) | (data2.targets== digit_q)]\n",
    "tt[tt==digit_p] =  0\n",
    "tt[tt==digit_q] = 1\n",
    "dd = data2.data[(data2.targets== digit_p) | (data2.targets== digit_q)] \n",
    "# tt = data.targets[(data.targets== 1)]\n",
    "# dd = data.data[(data.targets== 1)] \n",
    "\n",
    "data2.targets = tt\n",
    "data2.data = dd\n",
    "test_loader = torch.utils.data.DataLoader(data2, batch_size=100, shuffle=True, drop_last = True)\n",
    "# Num batches\n",
    "# num_batches = len(train_loader)\n",
    "print((tt==0).sum(), (tt==1).sum())\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# train_dataset.data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "28 2\n",
      "number of epochs: 83\n"
     ]
    }
   ],
   "source": [
    "p = train_dataset.data.size()[1]\n",
    "num_cls = len(set(train_dataset.targets.numpy()))\n",
    "print(p, num_cls)\n",
    "\n",
    "batch_size = 100\n",
    "n_iters = 10000\n",
    "num_epochs = int(n_iters / (len(train_dataset) / batch_size))+1\n",
    "print('number of epochs: {}'.format(num_epochs))\n",
    "\n",
    "train_loader = torch.utils.data.DataLoader(dataset=train_dataset, \n",
    "                                           batch_size=batch_size, \n",
    "                                           shuffle=True)\n",
    "\n",
    "test_loader = torch.utils.data.DataLoader(dataset=test_dataset, \n",
    "                                          batch_size=batch_size, \n",
    "                                          shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RNNModel(\n",
      "  (rnn): RNN(4, 2, num_layers=2, batch_first=True)\n",
      "  (fc): Linear(in_features=2, out_features=10, bias=True)\n",
      ")\n",
      "10\n",
      "torch.Size([2, 4])\n",
      "torch.Size([2, 2])\n",
      "torch.Size([2])\n",
      "torch.Size([2])\n",
      "torch.Size([2, 2])\n",
      "torch.Size([2, 2])\n",
      "torch.Size([2])\n",
      "torch.Size([2])\n",
      "torch.Size([10, 2])\n",
      "torch.Size([10])\n"
     ]
    }
   ],
   "source": [
    "class RNNModel(nn.Module):\n",
    "    def __init__(self, input_dim, hidden_dim, layer_dim, output_dim):\n",
    "        super(RNNModel, self).__init__()\n",
    "        # Hidden dimensions\n",
    "        self.hidden_dim = hidden_dim\n",
    "\n",
    "        # Number of hidden layers\n",
    "        self.layer_dim = layer_dim\n",
    "\n",
    "        # Building your RNN\n",
    "        # batch_first=True causes input/output tensors to be of shape\n",
    "        # (batch_dim, seq_dim, feature_dim)\n",
    "        self.rnn = nn.RNN(input_dim, hidden_dim, layer_dim, batch_first=True, nonlinearity='tanh')\n",
    "\n",
    "        # Readout layer\n",
    "        self.fc = nn.Linear(hidden_dim, output_dim)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # Initialize hidden state with zeros\n",
    "        #######################\n",
    "        #  USE GPU FOR MODEL  #\n",
    "        #######################\n",
    "        h0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).to(device)\n",
    "\n",
    "        # One time step\n",
    "        # We need to detach the hidden state to prevent exploding/vanishing gradients\n",
    "        # This is part of truncated backpropagation through time (BPTT)\n",
    "        out, hn = self.rnn(x, h0.detach())\n",
    "\n",
    "        # Index hidden state of last time step\n",
    "        # out.size() --> 100, 28, 100\n",
    "        # out[:, -1, :] --> 100, 100 --> just want last time step hidden states! \n",
    "        out = self.fc(out[:, -1, :]) \n",
    "        # out.size() --> 100, 10\n",
    "        return out\n",
    "\n",
    "input_dim = 4\n",
    "hidden_dim = 2\n",
    "layer_dim = 2  # ONLY CHANGE IS HERE FROM ONE LAYER TO TWO LAYER\n",
    "output_dim = 10\n",
    "\n",
    "model = RNNModel(input_dim, hidden_dim, layer_dim, output_dim)\n",
    "model.to(device)\n",
    "\n",
    "# JUST PRINTING MODEL & PARAMETERS \n",
    "print(model)\n",
    "print(len(list(model.parameters())))\n",
    "for i in range(len(list(model.parameters()))):\n",
    "    print(list(model.parameters())[i].size())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration: 500. Loss: 0.7144721746444702. Accuracy: 51.14427947998047\n",
      " Best model saved.\n",
      "Iteration: 1000. Loss: 0.7127699255943298. Accuracy: 51.14427947998047\n",
      "Iteration: 1500. Loss: 0.6995964646339417. Accuracy: 51.14427947998047\n",
      "Iteration: 2000. Loss: 0.7078530192375183. Accuracy: 51.14427947998047\n",
      "Iteration: 2500. Loss: 0.7059417963027954. Accuracy: 51.14427947998047\n",
      "Iteration: 3000. Loss: 0.7016143798828125. Accuracy: 51.14427947998047\n"
     ]
    }
   ],
   "source": [
    "### Model Training\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "learning_rate = 0.01\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  \n",
    "\n",
    "# Number of steps to unroll\n",
    "seq_dim = 28*28 // 4 \n",
    "\n",
    "iter = 0\n",
    "best_acc = 0\n",
    "num_epochs = 6\n",
    "for epoch in range(num_epochs):\n",
    "    for i, (images, labels) in enumerate(train_loader):\n",
    "        model.train()\n",
    "        # Load images as tensors with gradient accumulation abilities\n",
    "        images, labels = images.view(-1, seq_dim, \n",
    "                                     input_dim).requires_grad_().to(device), labels.to(device)\n",
    "\n",
    "        # Clear gradients w.r.t. parameters\n",
    "        optimizer.zero_grad()\n",
    "\n",
    "        # Forward pass to get output/logits\n",
    "        # outputs.size() --> 100, 10\n",
    "        outputs = model(images)\n",
    "\n",
    "        # Calculate Loss: softmax --> cross entropy loss\n",
    "        loss = criterion(outputs, labels)\n",
    "\n",
    "        # Getting gradients w.r.t. parameters\n",
    "        loss.backward()\n",
    "\n",
    "        # Updating parameters\n",
    "        optimizer.step()\n",
    "\n",
    "        iter += 1\n",
    "\n",
    "        if iter % 500 == 0:\n",
    "            model.eval()\n",
    "            # Calculate Accuracy         \n",
    "            correct = 0\n",
    "            total = 0\n",
    "            with torch.no_grad():\n",
    "                # Iterate through test dataset\n",
    "                for images, labels in test_loader:\n",
    "                    # Resize images\n",
    "                    images = images.view(-1, seq_dim, input_dim).to(device)\n",
    "\n",
    "                    # Forward pass only to get logits/output\n",
    "#                     import pdb; pdb.set_trace()\n",
    "                    outputs = model(images)\n",
    "\n",
    "                    # Get predictions from the maximum value\n",
    "                    _, predicted = torch.max(outputs.data, 1)\n",
    "\n",
    "                    # Total number of labels\n",
    "                    total += labels.size(0)\n",
    "\n",
    "                    # Total correct predictions\n",
    "                    if cuda:\n",
    "                        correct += (predicted.cpu() == labels.cpu()).sum()\n",
    "                    else:\n",
    "                        correct += (predicted == labels).sum()\n",
    "\n",
    "            accuracy = 100 * correct.float() / total\n",
    "\n",
    "            # Print Loss\n",
    "            print('Iteration: {}. Loss: {}. Accuracy: {}'.format(iter, loss.item(), accuracy))\n",
    "            if accuracy > best_acc:\n",
    "                best_acc = accuracy\n",
    "                torch.save({'epoch': epoch,\n",
    "                            'model': model.state_dict(),\n",
    "                            'optimizer': optimizer.state_dict()\n",
    "                           }, '{}/rnn_iter_{}.pth'.format(save_path, iter))\n",
    "                print('\\r Best model saved.\\r')\n",
    "      "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### Load and use the best model\n",
    "bst_mdl = save_path+'/rnn_best.pth'\n",
    "model.load_state_dict(torch.load(bst_mdl)['model'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([100, 1, 28, 28])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "images.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/5], Step [100/600], Loss: 0.4816\n",
      "Epoch [1/5], Step [200/600], Loss: 0.2950\n",
      "Epoch [1/5], Step [300/600], Loss: 0.2927\n",
      "Epoch [1/5], Step [400/600], Loss: 0.1200\n",
      "Epoch [1/5], Step [500/600], Loss: 0.1486\n",
      "Epoch [1/5], Step [600/600], Loss: 0.0772\n",
      "Epoch [2/5], Step [100/600], Loss: 0.0325\n",
      "Epoch [2/5], Step [200/600], Loss: 0.1791\n",
      "Epoch [2/5], Step [300/600], Loss: 0.1668\n",
      "Epoch [2/5], Step [400/600], Loss: 0.2146\n",
      "Epoch [2/5], Step [500/600], Loss: 0.0146\n",
      "Epoch [2/5], Step [600/600], Loss: 0.0834\n",
      "Epoch [3/5], Step [100/600], Loss: 0.0175\n",
      "Epoch [3/5], Step [200/600], Loss: 0.0621\n",
      "Epoch [3/5], Step [300/600], Loss: 0.1029\n",
      "Epoch [3/5], Step [400/600], Loss: 0.0541\n",
      "Epoch [3/5], Step [500/600], Loss: 0.1015\n",
      "Epoch [3/5], Step [600/600], Loss: 0.0447\n",
      "Epoch [4/5], Step [100/600], Loss: 0.0538\n",
      "Epoch [4/5], Step [200/600], Loss: 0.2240\n",
      "Epoch [4/5], Step [300/600], Loss: 0.1090\n",
      "Epoch [4/5], Step [400/600], Loss: 0.0375\n",
      "Epoch [4/5], Step [500/600], Loss: 0.0854\n",
      "Epoch [4/5], Step [600/600], Loss: 0.0608\n",
      "Epoch [5/5], Step [100/600], Loss: 0.0366\n",
      "Epoch [5/5], Step [200/600], Loss: 0.0681\n",
      "Epoch [5/5], Step [300/600], Loss: 0.0058\n",
      "Epoch [5/5], Step [400/600], Loss: 0.1727\n",
      "Epoch [5/5], Step [500/600], Loss: 0.0176\n",
      "Epoch [5/5], Step [600/600], Loss: 0.0923\n",
      "Test Accuracy of the model on the 10000 test images: 97.45 %\n"
     ]
    }
   ],
   "source": [
    "# import torch \n",
    "# import torch.nn as nn\n",
    "# import torchvision\n",
    "# import torchvision.transforms as transforms\n",
    "\n",
    "\n",
    "# # Device configuration\n",
    "# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "\n",
    "# # Hyper-parameters\n",
    "# sequence_length = 28\n",
    "# input_size = 28\n",
    "# hidden_size = 128\n",
    "# num_layers = 2\n",
    "# num_classes = 10\n",
    "# batch_size = 100\n",
    "# num_epochs = 5\n",
    "# learning_rate = 0.01\n",
    "\n",
    "\n",
    "# # Recurrent neural network (many-to-one)\n",
    "# class RNN(nn.Module):\n",
    "#     def __init__(self, input_size, hidden_size, num_layers, num_classes):\n",
    "#         super(RNN, self).__init__()\n",
    "#         self.hidden_size = hidden_size\n",
    "#         self.num_layers = num_layers\n",
    "#         self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)\n",
    "#         self.fc = nn.Linear(hidden_size, num_classes)\n",
    "    \n",
    "#     def forward(self, x):\n",
    "#         # Set initial hidden and cell states \n",
    "#         h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(device) \n",
    "#         c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(device)\n",
    "        \n",
    "#         # Forward propagate LSTM\n",
    "#         out, _ = self.lstm(x, (h0, c0))  # out: tensor of shape (batch_size, seq_length, hidden_size)\n",
    "        \n",
    "#         # Decode the hidden state of the last time step\n",
    "#         out = self.fc(out[:, -1, :])\n",
    "#         return out\n",
    "\n",
    "# model = RNN(input_size, hidden_size, num_layers, num_classes).to(device)\n",
    "\n",
    "\n",
    "# # Loss and optimizer\n",
    "# criterion = nn.CrossEntropyLoss()\n",
    "# optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)\n",
    "\n",
    "# # Train the model\n",
    "# total_step = len(train_loader)\n",
    "# for epoch in range(num_epochs):\n",
    "#     for i, (images, labels) in enumerate(train_loader):\n",
    "#         images = images.reshape(-1, sequence_length, input_size).to(device)\n",
    "#         labels = labels.to(device)\n",
    "        \n",
    "#         # Forward pass\n",
    "#         outputs = model(images)\n",
    "#         loss = criterion(outputs, labels)\n",
    "        \n",
    "#         # Backward and optimize\n",
    "#         optimizer.zero_grad()\n",
    "#         loss.backward()\n",
    "#         optimizer.step()\n",
    "        \n",
    "#         if (i+1) % 100 == 0:\n",
    "#             print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}' \n",
    "#                    .format(epoch+1, num_epochs, i+1, total_step, loss.item()))\n",
    "\n",
    "# # Test the model\n",
    "# with torch.no_grad():\n",
    "#     correct = 0\n",
    "#     total = 0\n",
    "#     for images, labels in test_loader:\n",
    "#         images = images.reshape(-1, sequence_length, input_size).to(device)\n",
    "#         labels = labels.to(device)\n",
    "#         outputs = model(images)\n",
    "#         _, predicted = torch.max(outputs.data, 1)\n",
    "#         total += labels.size(0)\n",
    "#         correct += (predicted == labels).sum().item()\n",
    "\n",
    "#     print('Test Accuracy of the model on the 10000 test images: {} %'.format(100 * correct / total)) \n",
    "\n",
    "# # Save the model checkpoint\n",
    "# torch.save(model.state_dict(), 'model.ckpt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100%|██████████| 100/100 [01:04<00:00,  1.55it/s]\n"
     ]
    }
   ],
   "source": [
    "### Feeding white-noise\n",
    "model.eval()\n",
    "\n",
    "batch_size = 10000\n",
    "all_size = 100000\n",
    "iters = 100\n",
    "stats = {} # recording the bias\n",
    "noise = {}\n",
    "\n",
    "for i in range(num_cls):\n",
    "    stats[i] = 0\n",
    "    noise[i] = []\n",
    "\n",
    "with tqdm(total=iters, file=sys.stdout) as pbar:\n",
    "    for kk in range(iters):\n",
    "        z = torch.rand(all_size, p, p)\n",
    "        for k in range(0, all_size, batch_size):\n",
    "            with torch.no_grad():\n",
    "                cur_data = z[k:k+batch_size]\n",
    "                if cuda:\n",
    "                    cur_data = cur_data.cuda()\n",
    "                pred = model(cur_data).max(1)[1]\n",
    "\n",
    "            for i in range(num_cls):\n",
    "                noise[i].append(cur_data[pred == i].cpu())\n",
    "                stats[i] += (pred == i).sum().cpu()\n",
    "        pbar.update(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{0: tensor(3190130), 1: tensor(6809870)}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "stats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([9, 8, 9,  ..., 8, 8, 8], device='cuda:0')"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# visualize\n",
    "for i in range(num_cls):\n",
    "    if stats[i] != 0:\n",
    "        print(i)\n",
    "        plt.imshow(torch.cat(noise[i]).mean(0))\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
